package com.fessc.develop.base;

import com.fessc.develop.utils.ExceptionUtil;
import com.fessc.develop.utils.KeyGenerateUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.codec.binary.Base64;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

@Slf4j
@Getter
@Setter
public class RsaTool {
    private String keyAlgorithm = "RSA";
    private String signAlgorithm = "SHA1withRSA";
    private String signLength = "1024";
    private String priCer;
    private String pubCer;
    private PublicKey pubKey;
    private PrivateKey priKey;
    public static int MAX_ENCRYPT_BLOCK = 117;
    public static int MAX_DECRYPT_BLOCK = 128;

    public RsaTool() {

    }

    public RsaTool(PublicKey pubKey, PrivateKey priKey) {
        this.pubKey = pubKey;
        this.priKey = priKey;

    }

    public byte[] subArray(byte[] arr, int beginIndex, int endIndex) {
        byte[] result = new byte[endIndex - beginIndex];
        for (int i = beginIndex; i < endIndex; i++) {
            result[i - beginIndex] = arr[i];
        }
        return result;
    }

    // 公钥加密
    public byte[] encrypt(byte[] content) throws Exception {
        ByteArrayOutputStream out = null;
        byte[] encryptedData = null;
        try {
            PublicKey publicKey = this.pubKey;
            Cipher cipher = Cipher.getInstance("RSA");// java默认"RSA"="RSA/ECB/PKCS1Padding"
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            out = new ByteArrayOutputStream();
            for (int i = 0; i < content.length; i = i + 117) {
                byte bt[] = cipher.doFinal(subArray(content, i, Math.min(i + 117, content.length)));
                out.write(bt,0,bt.length);
            }
            encryptedData = out.toByteArray();
        } catch (Exception e) {
            log.info("公钥加密异常：" + e.getMessage());
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (Exception e) {
                    log.info("关闭输出流异常：" + e.getMessage());
                }
            }
        }
        return encryptedData;
    }

    // 私钥解密
    public byte[] decrypt(byte[] content) throws Exception {
        ByteArrayOutputStream out = null;
        byte[] decryptedData = null;
        try {
            byte[] cache;
            out = new ByteArrayOutputStream();
            PrivateKey privateKey = this.priKey;
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            for (int i = 0; i < content.length; i = i + 128) {
                cache = cipher.doFinal(subArray(content, i, Math.min(i + 128, content.length)));
                out.write(cache, 0, cache.length);
            }
            decryptedData = out.toByteArray();
        } catch (Exception e) {
            log.info("私钥解密异常：" + e.getMessage());
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (Exception e) {
                    log.info("关闭输出流异常：" + e.getMessage());
                }
            }
        }
        return decryptedData;
    }

    // 公钥加密
    public String encrypt(String body)
    {
        ByteArrayOutputStream out = null;
        try {
            //PublicKey publicKey = this.GeneratePublicKey();
            Cipher cipher = Cipher.getInstance(keyAlgorithm);// java默认"RSA"="RSA/ECB/PKCS1Padding"
            cipher.init(Cipher.ENCRYPT_MODE, this.pubKey);

            byte content[] = body.getBytes("utf-8"); // 对方接收是UTF-8编码，要指定
            int inputLen = content.length;
            out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段解密  
            while (inputLen - offSet > 0)
            {
                if (inputLen - offSet > MAX_ENCRYPT_BLOCK)
                {
                    cache = cipher.doFinal(content, offSet, MAX_ENCRYPT_BLOCK);
                }
                else
                {
                    cache = cipher.doFinal(content, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_ENCRYPT_BLOCK;
            }

            byte[] rt = out.toByteArray();
            BASE64Encoder encode = new BASE64Encoder();
            return encode.encode(rt);
        } catch (Exception e) {
            throw ExceptionUtil.businessException("EE4008");
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (Exception e) {
                    log.info("关闭文件流异常：" + e.getMessage());
                }
            }
        }
    }

    // 私钥解密
    public String decrypt(String body)
    {
        ByteArrayOutputStream out = null;
        try {
            //PrivateKey privateKey = this.GeneratePrivateKey();
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, this.priKey);

            BASE64Decoder decode = new BASE64Decoder();
            byte[] b = decode.decodeBuffer(body);
            int inputLen = b.length;
            out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段解密  
            while (inputLen - offSet > 0)
            {
                if (inputLen - offSet > MAX_DECRYPT_BLOCK)
                {
                    cache = cipher.doFinal(b, offSet, MAX_DECRYPT_BLOCK);
                }
                else
                {
                    cache = cipher.doFinal(b, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_DECRYPT_BLOCK;
            }
            byte[] rt = out.toByteArray();
            return new String(rt,"utf-8");  // 对方发送过去是UTF-8编码，要指定回来
        } catch (Exception e) {
            throw ExceptionUtil.businessException("EE4007");
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (Exception e) {
                    log.info("关闭文件流异常：" + e.getMessage());
                }
            }
        }
    }

    /**
     * 获取公钥
     *
     * @return
     * @throws Exception
     */
    private PublicKey GeneratePublicKey() throws Exception {
        FileInputStream fin = null;
        try {
            fin = new FileInputStream(pubCer);
            byte[] encodedpubkey = new byte[fin.available()];
            int keyLength = fin.read(encodedpubkey);
            X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(encodedpubkey);
            KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
            PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);
            return pubKey;
        } catch (Exception e) {
            log.info("获取公钥异常：" + e.getMessage());
            throw e;
        } finally {
            if (fin != null) {
                try {
                    fin.close();
                } catch (Exception e) {
                    log.info("关闭文件流异常：" + e.getMessage());
                }
            }
        }
    }

    /**
     * 获取私钥
     *
     * @return
     * @throws Exception
     */
    private PrivateKey GeneratePrivateKey() throws Exception {
        FileInputStream fin = null;
        try {
            fin = new FileInputStream(priCer);
            byte[] encodedprikey = new byte[fin.available()];
            int keyLength = fin.read(encodedprikey);
            PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec(encodedprikey);
            KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
            PrivateKey priKey = keyFactory.generatePrivate(priKeySpec);
            return priKey;
        } catch (Exception e) {
            log.info("获取私钥异常：" + e.getMessage());
            throw e;
        } finally {
            if (fin != null) {
                try {
                    fin.close();
                } catch (Exception e) {
                    log.info("关闭文件流异常：" + e.getMessage());
                }
            }
        }
    }

    public static void main(String[] args) {
        String test = "{\"CHECK_DATE\":\"20150122\",\"CHECK_TYPE\":\"1\",\"CHECK_CHANNEL\":\"16\"}";
        log.info("请求明文报文："+test);
        String afterRsa = null;
        KeyPair keyPair = KeyGenerateUtil.generateKeyPair();
        RsaTool rsaUtil = new RsaTool(keyPair.getPublic(), keyPair.getPrivate());

        try {
            afterRsa = Base64.encodeBase64String(rsaUtil.encrypt(test.getBytes(StandardCharsets.UTF_8)));
            log.info("请求报文加密结果："+afterRsa);
        } catch (Exception e) {
            log.info(e.getMessage());
        }

        try {
            assert afterRsa != null;
            String result = new String(rsaUtil.decrypt(Base64.decodeBase64(afterRsa)));
            log.info("请求报文解密结果："+result);
            log.info("请求加密解密结果："+(test.equals(result)?"一致":"不一致"));
        } catch (Exception e) {
            log.info(e.getMessage());
        }
    }

}
